# coding=utf8
from pyjvm.classfile.clstype import *
from pyjvm.classfile.clsfile import *
from pyjvm.classfile.clsmember import *
from pyjvm.classfile.attrs import *
from pyjvm.classfile.reader import ClassBytesReader
from pyjvm.classfile.constpool import ConstantPool, T_ConstInfo, CONSTANT_INFO_MAP, ConstantInfoTag
from pyjvm.classfile.ex import ClassFileMagicError, PyJvmUnsupportedClassVersionError
from typing import List


class ClassFileChecker(object):
    @staticmethod
    def assert_magic(magic):
        if magic != ClassFile.MAGIC:
            raise ClassFileMagicError(
                '无效的class文件，读取到magic：%s，magic必须为：%s' % (
                    magic, ClassFile.MAGIC))

    @staticmethod
    def assert_version(major, minor):
        if major < JavaMajorVersion.JAVA1_2 or major > JavaMajorVersion.JAVA8:
            raise PyJvmUnsupportedClassVersionError(
                'PyJvm: 不支持的 java major.minor version: %d.%d' % (major, minor)
            )


class JavaMajorVersion:
    JAVA1_2 = 46  # java 1.2 ~ 1.4
    JAVA1_3 = 47
    JAVA1_4 = 48
    JAVA5 = 49
    JAVA6 = 50
    JAVA7 = 51
    JAVA8 = 52


class ClassFileReader:

    def __init__(self, reader: ClassBytesReader):
        self._reader = reader

    def read_magic(self) -> bytes:
        return self._reader.u4()

    def read_minor_version(self) -> u2:
        return int16(self._reader.u2())

    def read_major_version(self) -> u2:
        return int16(self._reader.u2())

    def read_const_pool_count(self) -> int:
        return self._reader.uint16()

    def read_const_pool_info(self, cp: ConstantPool) -> T_ConstInfo:
        tag = self._reader.uint8()
        info_type = CONSTANT_INFO_MAP[tag]
        info = info_type()
        arg_count = info.parse.__code__.co_argcount

        if arg_count > 2:
            info.parse(self._reader, cp)
        else:
            info.parse(self._reader)
        return info

    def read_access_flags(self) -> u2:
        return self._reader.u2()

    def read_this_class_index(self) -> u2:
        return self._reader.u2()

    def read_super_class_name_index(self) -> u2:
        return self._reader.u2()

    def read_interfaces_idx_list(self) -> List[u2]:
        return self._reader.u2s()

    def read_fields_count(self) -> int:
        return self._reader.uint16()

    def read_field(self, cp: ConstantPool) -> FieldInfo:
        info = FieldInfo()
        info.new(self._reader, cp)
        return info

    def read_methods_count(self) -> int:
        return self._reader.uint16()

    def read_method(self, cp: ConstantPool) -> MethodInfo:
        info = MethodInfo()
        info.new(self._reader, cp)
        return info

    def read_attributes(self, cp: ConstantPool) -> List[AttributeInfo]:
        return AttributeInfo.read_attributes(self._reader, cp)


class ClassFileParser:

    def __init__(self, class_bytes: bytes):
        self.__reader = ClassFileReader(ClassBytesReader(class_bytes))
        self.__file = ClassFile()

    def __parse_magic(self):
        magic = self.__reader.read_magic()
        ClassFileChecker.assert_magic(magic)
        self.__file.magic = magic

    def __parse_version(self):
        minor = self.__reader.read_minor_version()
        major = self.__reader.read_major_version()
        ClassFileChecker.assert_version(major, minor)
        self.__file.major_version = major
        self.__file.minor_version = minor

    def __parse_const_pool(self):
        count = self.__reader.read_const_pool_count()
        pool = ConstantPool()
        skip = False
        for i in range(1, count):
            if skip:
                pool.append(None)
                skip = False
                continue
            info = self.__reader.read_const_pool_info(pool)
            pool.append(info)
            skip = info.tag in ConstantInfoTag.WideTagSet
        self.__file.const_pool = pool

    def __parse_access_flags(self):
        self.__file.access_flags = self.__reader.read_access_flags()

    def __parse_this_class(self):
        self.__file.this_class_idx = self.__reader.read_this_class_index()

    def __parse_super_class(self):
        self.__file.super_class_idx = self.__reader.read_super_class_name_index()

    def __parse_interfaces(self):
        self.__file.interface_idx_list = self.__reader.read_interfaces_idx_list()

    def __parse_fields(self):
        count = self.__reader.read_fields_count()
        self.__file.fields = [self.__reader.read_field(self.__file.const_pool)
                              for _ in range(count)]

    def __parse_methods(self):
        count = self.__reader.read_fields_count()
        self.__file.methods = [self.__reader.read_method(self.__file.const_pool)
                               for _ in range(count)]

    def __parse_attributes(self):
        self.__file.attributes = self.__reader.read_attributes(self.__file.const_pool)

    def parse(self) -> ClassFile:
        self.__parse_magic()
        self.__parse_version()
        self.__parse_const_pool()
        self.__parse_access_flags()
        self.__parse_this_class()
        self.__parse_super_class()
        self.__parse_interfaces()
        self.__parse_fields()
        self.__parse_methods()
        self.__parse_attributes()
        return self.__file


__all__ = ['ClassFileParser']
